/* 
 * Copyright (c) 2009, Giora Kosoi giorak@gmail.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Giora Kosoi ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Giora Kosoi BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sparrow/variant_headers.h>
#include <sparrow/errors.h>

VariantSet::VariantSet()
    :   Variant(VT_VARIANT_SET)
{
}

VariantSet::~VariantSet()
{
    clear();
}

Variant *VariantSet::get(const std::string &key) const
{
    Variant *v = get(key, std::nothrow);
    if(v == NULL)
    {
        throw_error<std::runtime_error>(__FILE__, __LINE__,
                "preventing access to NULL value associated with key [%s], " 
                "if intentional call get(const std::string &, const std::nothrow_t &)",
                key.c_str());
    }
    return v;
}

Variant *VariantSet::get(const std::string &key, const std::nothrow_t &) const
{
    var_string_map::const_iterator i = vsm.find(key); 
    if(i == vsm.end())
    {
        return NULL;
    }
    return i->second;
}

void VariantSet::set(const std::string &key, Variant *v)
{
    std::pair<var_string_map::iterator, bool> result = vsm.insert(std::make_pair(key, v));
    if(result.second == false)
    {
        delete result.first->second;
        result.first->second = v;
    }
}

void VariantSet::remove(const std::string &key)
{
    var_string_map::iterator i = vsm.find(key); 
    if(i != vsm.end())
    {
        delete i->second;
        vsm.erase(i);
    }
}

Variant *VariantSet::get(const IntKey &key) const
{
    Variant *v = get(key, std::nothrow);
    if(v == NULL)
    {
        throw_error<std::runtime_error>(__FILE__, __LINE__,
                "preventing access to NULL value associated with key [%lld], " 
                "if intentional call get(const IntKey &, const std::nothrow_t &)",
                key.value());
    }
    return v;
}

Variant *VariantSet::get(const IntKey &key, const std::nothrow_t &) const
{
    var_int_map::const_iterator i = vim.find(key); 
    if(i == vim.end())
    {
        return NULL;
    }
    return i->second;
}

void VariantSet::set(const IntKey &key, Variant *v)
{
    std::pair<var_int_map::iterator, bool> result = vim.insert(std::make_pair(key, v));
    if(result.second == false)
    {
        delete result.first->second;
        result.first->second = v;
    }
}

void VariantSet::remove(const IntKey &key)
{
    var_int_map::iterator i = vim.find(key); 
    if(i != vim.end())
    {
        delete i->second;
        vim.erase(i);
    }
}

Variant *VariantSet::get(size_t index) const
{
    Variant *v =  get(index, std::nothrow);
    if(v == NULL)
    {
        throw_error<std::runtime_error>(__FILE__, __LINE__,
                "preventing access to NULL value associated with index [%d], " 
                "if intentional call get(const std::string &, const std::nothrow_t &)",
                index); 
    }
    return v;
}

Variant *VariantSet::get(size_t index, const std::nothrow_t &) const
{
    return vv.size() > index ? vv.at(index) : NULL;
}

void VariantSet::grow(size_t new_size)
{
    if(vv.size() < new_size)
    {
        vv.resize(new_size, NULL);
    }
}

void VariantSet::set(size_t index, Variant *v)
{
    grow(index + 1);
    delete vv.at(index);
    vv.at(index) = v;
}

void VariantSet::insert(size_t index, Variant *v)
{
    grow(index);
    vv.insert(vv.begin() + index, v);
}

void VariantSet::insert(Variant *v)
{
    vv.insert(vv.end(), v);
}

void VariantSet::remove(size_t index)
{
    if(index >= vv.size())
    {
        return;
    }

    Variant *v = vv.at(index);
    delete(v);
    vv.erase(vv.begin() + index);
}

size_t VariantSet::array_size() const
{
    return vv.size();
}

size_t VariantSet::size() const
{
    return vv.size() + vsm.size() + vim.size();
}

void VariantSet::clear()
{
    var_vector::iterator i;
    for(i = vv.begin(); i < vv.end(); ++i)
    {
        delete *i;
    }
    vv.erase(vv.begin(), vv.end());

    var_string_map::iterator i2;
    for(i2 = vsm.begin(); i2 != vsm.end(); ++i2)
    {
        delete i2->second;
    }
    vsm.clear();

    var_int_map::iterator i3;
    for(i3 = vim.begin(); i3 != vim.end(); ++i3)
    {
        delete i3->second;
    }
    vim.clear();
}

Variant *VariantSet::clone() const
{
    VariantSet *v = new VariantSet;
    var_vector::const_iterator i;
    for(i = vv.begin(); i < vv.end(); ++i)
    {
        v->insert((*i)->clone());
    }

    var_string_map::const_iterator i2;
    for(i2 = vsm.begin(); i2 != vsm.end(); ++i2)
    {
        v->set(i2->first, i2->second->clone());
    }

    var_int_map::const_iterator i3;
    for(i3 = vim.begin(); i3 != vim.end(); ++i3)
    {
        v->set(i3->first, i3->second->clone());
    }
    return v;
}

const VariantSet *VariantSet::variant_set() const
{
    return this;
}

VariantSet *VariantSet::variant_set()
{
    return this;
}

void VariantSet::add_pvar(const Variant *v) const
{
    if(v == NULL)
    {
        ps_.insert(ps_.size(), "NULL");
    }
    else
    {
        ps_.insert(ps_.size(), v->pstr());
    }
}

void VariantSet::remove_comma() const
{
    if(ps_.size() > 2 &&
       ps_[ps_.size() - 2] == ',' &&
       ps_[ps_.size() - 1] == ' ')
    {
        ps_.resize(ps_.size() - 2);
    }
}

const std::string &VariantSet::pstr() const
{
    if(ps_.empty())
    {
        ps_.insert(ps_.size(), "[");
        // array part
        for(int i = 0; i < vv.size(); ++i)
        {
            add_pvar(vv.at(i));
            ps_.insert(ps_.size(), ", ");
        }
        remove_comma();

        if(vv.size() > 0)
        {
            ps_.insert(ps_.size(), "; ");
        }

        var_int_map::const_iterator vimi;
        for(vimi = vim.begin(); vimi != vim.end(); ++vimi)
        {
            VariantInt64 key(vimi->first.value());
            ps_.insert(ps_.size(), key.str());
            ps_.insert(ps_.size(), " : ");
            ps_.insert(ps_.size(), vimi->second->pstr());
            ps_.insert(ps_.size(), ", ");
        }

        var_string_map::const_iterator vsmi;
        for(vsmi = vsm.begin(); vsmi != vsm.end(); ++vsmi)
        {
            ps_.insert(ps_.size(), "\"");
            ps_.insert(ps_.size(), vsmi->first);
            ps_.insert(ps_.size(), "\"");
            ps_.insert(ps_.size(), " : ");
            ps_.insert(ps_.size(), vsmi->second->pstr());
            ps_.insert(ps_.size(), ", ");
        }
        remove_comma();
        ps_.insert(ps_.size(), "]");
    }
    return ps_;
}

Variant *VariantSet::release(const std::string &key)
{
    Variant *v = release(key, std::nothrow);
    if(v == NULL)
    {
        throw_error<std::runtime_error>(__FILE__, __LINE__,
                "preventing access to NULL value associated with key [%s], "
                "if intentional call release(const std::string &, const std::nothrow_t &)",
                key);
    }
    return v;
}

Variant *VariantSet::release(const IntKey &key)
{
    Variant *v = release(key, std::nothrow);
    if(v == NULL)
    {
        throw_error<std::runtime_error>(__FILE__, __LINE__,
                "preventing access to NULL value associated with key [%lld], "
                "if intentional call release(const IntKey &, const std::nothrow_t &)",
                key.value());
    }
    return v;
}

Variant *VariantSet::release(size_t index)
{
    Variant *v = release(index, std::nothrow);
    if(v == NULL)
    {
        throw_error<std::runtime_error>(__FILE__, __LINE__,
                "preventing access to NULL value associated with index [%d], "
                "if intentional call release(size_t, const std::nothrow_t &)",
                index);
    }
    return v;
} 

Variant *VariantSet::release(const std::string &key, const std::nothrow_t &)
{
    var_string_map::iterator i = vsm.find(key);
    if(i != vsm.end())
    {
        Variant *v = i->second;
        i->second = NULL;
        vsm.erase(i);
        return v;
    }
    return NULL;
}

Variant *VariantSet::release(const IntKey &key, const std::nothrow_t &)
{
    var_int_map::iterator i = vim.find(key);
    if(i != vim.end())
    {
        Variant *v = i->second;
        i->second = NULL;
        vim.erase(i);
        return v;
    }
    return NULL;
}

Variant *VariantSet::release(size_t index, const std::nothrow_t &)
{
    if(index < vv.size())
    {
        Variant *v = vv.at(index);
        vv.erase(vv.begin() + index);
        return v;
    }

    return NULL;
}

